#region Using Statements
using System;
using System.Diagnostics;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using System.Reflection;

using Agro2D;
//using Agro2DPipeline;
#endregion

#region License
//-----------------------------------------------------------------------------
// Copyright (c) 2008, Aaron MacDougall, Daniel Jeffery
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
//   this list of conditions and the following disclaimer.
//
// * Redistributions in binary form must reproduce the above copyright notice,
//   this list of conditions and the following disclaimer in the documentation
//   and/or other materials provided with the distribution.
//
// * Neither the name of Aaron MacDougall or Daniel Jeffery nor the names of its contributors may
//   be used to endorse or promote products derived from this software without
//   specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
//-----------------------------------------------------------------------------
#endregion

namespace Agro2DTestbed
{
    public class TestSprite : Sprite
    {
        float blurTime = 0;
        float BLUR_DURAATION = 0.1f;

        List<SpriteProperties> prevSpriteProperties = new List<SpriteProperties>(10);
        int currentSlot = 0;

        public TestSprite(SpriteManager spriteman)
            : base(spriteman)
        { 

        }

        public TestSprite(
            SpriteManager spriteManager,
            string type,
            SpriteProperties spriteProperties,
            CollisionProperties collisionProperties,
            IAnimation animation
            )
        {
            Debug.Assert(spriteManager != null);

            this.SpriteManager = spriteManager;
            Type = type;
            SpriteProperties = spriteProperties;
            CollisionProperties = collisionProperties;
            this.Animation = animation;
        }

        public override void Update(GameTime gameTime)
        {
            blurTime += (float)gameTime.ElapsedGameTime.TotalSeconds;
            if(blurTime > BLUR_DURAATION)
            {
                if(currentSlot > 9) currentSlot = 0;
                if(prevSpriteProperties.Count < 10)
                {
                    prevSpriteProperties.Add(SpriteProperties);
                }
                prevSpriteProperties[currentSlot] = (SpriteProperties)SpriteProperties.Clone();
                currentSlot++;
                blurTime = 0;
            }


            base.Update(gameTime);
        }

        public override void Draw(SpriteBatch spriteBatch, Rectangle viewport, float scale)
        {
            // Save current spriteProps
            SpriteProperties spriteProps = SpriteProperties;

            // should be under
            for(int i = 0; i < prevSpriteProperties.Count; i++)
            {
                SpriteProperties = (SpriteProperties)prevSpriteProperties[i].Clone();
                SpriteProperties.Tint = new Color(255, 255, 255, (byte)(105 - (i * 10)));
                base.Draw(spriteBatch, viewport, scale);
            }

            SpriteProperties = spriteProps;

            base.Draw(spriteBatch, viewport, scale);
        }

        public override object Clone()
        {
            IAnimation animation = null;

            if(this.Animation != null)
                animation = (IAnimation)this.Animation.Clone();

            TestSprite clonedObject = new TestSprite(
                this.SpriteManager,
                this.Type,
                (SpriteProperties)this.SpriteProperties.Clone(),
                (CollisionProperties)this.CollisionProperties.Clone(),
                animation
                );

            clonedObject.Drawable = this.Drawable;
            clonedObject.LayerNum = this.LayerNum;

            return clonedObject;
        }
    }

    public class TestLevel : Level
    {
        public TestLevel(SpriteManager man)
            : base(man)
        {
        }
    }

    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Agro2DTestbed : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        SpriteBatch spriteBatch;
        Sprite sprite;
        EffectSpill effectSpill;
        Rectangle viewPort = new Rectangle(0, 0, 1024, 768);
        LevelManager levelManager;

        SpriteManager spriteManager;
        float scale = 0.7f;

        public Agro2DTestbed()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";            
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            Log.Instance.LogMessage("Testbed init");

            // TODO: Add your initialization logic here
            this.spriteManager = new SpriteManager();

            TextureManager.Instance.Initialize(Content, "");

            levelManager = new LevelManager(this.spriteManager);

            SpriteFactory.Instance.AddSpriteType("TestSprite", new TestSprite(spriteManager));
            SpriteFactory.Instance.AddSpriteType("EffectSpill", new EffectSpill(spriteManager));
            SpriteFactory.Instance.AddSpriteType("Sprite", new Sprite(spriteManager));
            levelManager.RegisterLevelType("TestLevel", new TestLevel(spriteManager));
            AnimationFactory.Instance.AddAnimationType("MFrameAnimation", new MFrameAnimation());
            AnimationFactory.Instance.AddAnimationType("TimedMoveAnimation", new TimedMoveAnimation());

            levelManager.LoadGameFile("Test", this);
            levelManager.LoadLevel("Content/Test1");

            //sprite = this.spriteManager.GetSprite("spawnEffect");

            sprite = this.spriteManager.GetSprite("player1");
            effectSpill = (EffectSpill)this.spriteManager.GetSprite("tarSpill");

            //((ActionAnimation)sprite.Animation).PlayAction("spawn");

            //((ActionAnimation)sprite.Animation).PlayAction("rotate");

            // Testing animations and SpriteFactory (without Level).
            //sprite = SpriteFactory.Instance.CreateSprite("player1");
            //((ActionAnimation)sprite.Animation).PlayAction("blink");
            //this.spriteManager.AddDrawable(sprite, 3);

            // PREVIOUS -temp comment
            //Type spriteType = Assembly.GetEntryAssembly().GetType("Agro2DPipeline.GameImporter");
            //GameImporter gameimp = (Sprite)Activator.CreateInstance(spriteType, null);

            //Log.Instance.LogMessage("Testbed initialised");
            base.Initialize();
        }

        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            TextureManager.Instance.LoadContent();
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);
            DebugText.Instance.Load("DefaultFont", Content);
            
            // TODO: use this.Content to load your game content here
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        int currentAnim = 0;
        GamePadState prevpadState;
        GamePadDPad dpadState;
        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {
            // Allows the game to exit
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed
                || Keyboard.GetState().IsKeyDown(Keys.Escape))
                this.Exit();

            KeyboardState keyState = Keyboard.GetState();
            GamePadState padState = GamePad.GetState(PlayerIndex.One);

            if(dpadState != padState.DPad)
            {
                if(padState.DPad.Right == ButtonState.Pressed)
                    currentAnim++;
                if(padState.DPad.Left == ButtonState.Pressed)
                    currentAnim--;
            }

            dpadState = padState.DPad;

            //if(currentAnim > 6)
            //    currentAnim = 6;
            //if(currentAnim < 1)
            //    currentAnim = 1;

            if(keyState.IsKeyDown(Keys.D1))
            {
                sprite.SpriteProperties.IsVisible = true;
                ((ActionAnimation)sprite.Animation).PlayAction("spawn");
            }
            if(keyState.IsKeyDown(Keys.D2) || currentAnim == 2)
                ((ActionAnimation)sprite.Animation).PlayAction("move");
            if(keyState.IsKeyDown(Keys.D3) || currentAnim == 3)
                ((ActionAnimation)sprite.Animation).PlayAction("rotate");
            if(keyState.IsKeyDown(Keys.D4) || currentAnim == 4)
                ((ActionAnimation)sprite.Animation).PlayAction("rotateidle");
            if(keyState.IsKeyDown(Keys.D5) || currentAnim == 5)
                ((ActionAnimation)sprite.Animation).PlayAction("rotatemove");
            if(keyState.IsKeyDown(Keys.D6) || currentAnim == 6)
                ((ActionAnimation)sprite.Animation).PlayAction("Test34");

            if(keyState.IsKeyDown(Keys.Space))
                effectSpill.Spill();

            if(keyState.IsKeyDown(Keys.Up))
            {
                this.scale += 0.05f;
            }
            if(keyState.IsKeyDown(Keys.Down))
            {
                this.scale -= 0.05f;
            }



            //DebugText.Instance.Write("Action:", ((ActionAnimation)sprite.Animation).GetCurrentAnimationName());
            this.spriteManager.Update(gameTime);

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            graphics.GraphicsDevice.Clear(Color.CornflowerBlue);
            spriteBatch.Begin();
            this.spriteManager.Draw(spriteBatch, viewPort, scale);
            DebugText.Instance.Draw(spriteBatch, new Vector2(5,5));
            spriteBatch.End();

            base.Draw(gameTime);
        }
    }
}
